讓我們從Unity文檔抓一張完整的生命週期圖。
生 老 病 死 - 是人類的生命週期。
我們從一個人年輕到死亡的生命過程中
看懂一個人的一生。
在學習任何程式之前,
我們也要先明白屬於它的生命週期。
在 Unity 中,
天地之初,一切的故事
從Awake開始:
Scripts Execution Order:
但是有那麼多個Start,
要怎麼判斷哪個Start先執行呢?
你可以去Project Settings -> Scripts Execution Order 調整順序:
當天地初始化後
四季開始自己不斷運行:
Coroutine:
協程Coroutine 可以讓我們在主要工作中偷懶去泡一杯咖啡。
yield WaitForSeconds:等待X秒的時間。
yield StartCoroutine:等待另一個人泡完咖啡的時間。
如果你不是用Coroutine,
而是使用Async, Task的方式泡咖啡
要注意更新UI畫面的時候要切換回主要的線程:
Unity Main Thread Dispatcher
強制切回主線程的Github代碼
特殊情況:
Unity 的生命屬性(Attributes):
在 Unity 中,
你可以掛一些帽子(Attributes)在代碼上面
這樣就可以在沒有把代碼放在場景的情況下也可以執行:
[InitializeOnLoad]:讓腳本在編輯器加載時執行。
[ExecuteInEditMode]:讓腳本在編輯模式下也能執行,而不僅僅是在遊戲運行時。
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)]:
在SplashScreen之前執行
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]:
在Awake之前執行
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
在Awake之後執行
[RuntimeInitializeOnLoadMethod]
第一個場景完成讀取之後執行
class MyClass
{
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)]
static void OnBeforeSplashScreen()
{
Debug.Log("Before SplashScreen is shown and before the first scene is loaded.");
}
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void OnBeforeSceneLoad()
{
Debug.Log("First scene loading: Before Awake is called.");
}
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
static void OnAfterSceneLoad()
{
Debug.Log("First scene loaded: After Awake is called.");
}
[RuntimeInitializeOnLoadMethod]
static void OnRuntimeInitialized()
{
Debug.Log("Runtime initialized: First scene loaded: After Awake is called.");
}
}
以下是測試完整的生命流程的代碼:
using UnityEngine;
using System.Collections;
public class LifecycleMonitor : MonoBehaviour
{
// 在這裡定義變量
public float speed = 5f;
private Rigidbody rb;
// Awake 函數在所有其他指令之前執行
void Awake()
{
// 初始化變量
rb = GetComponent<Rigidbody>();
Debug.Log("Awake: 初始化變量");
}
// OnEnable 函數在物體被啟用時執行
void OnEnable()
{
Debug.Log("OnEnable: 物體被啟用");
}
// Start 函數在第一次畫面更新之前執行
void Start()
{
Debug.Log("Start: 遊戲開始");
// 啟動協程
StartCoroutine(MyCoroutine());
}
// Update 函數每一幀都會執行一次
void Update()
{
// 獲取玩家輸入
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
// 計算移動方向
Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
// 移動物體
rb.AddForce(movement * speed);
Debug.Log("Update: 每一幀都在執行");
}
// FixedUpdate 函數每一幀都會執行一次,通常用來處理物理運算
void FixedUpdate()
{
// 在這裡處理物理運算
Debug.Log("FixedUpdate: 處理物理運算");
}
// LateUpdate 函數在所有 Update 函數執行完之後執行
void LateUpdate()
{
Debug.Log("LateUpdate: 在所有 Update 之後執行");
}
// OnApplicationPause 函數在遊戲暫停時執行
void OnApplicationPause(bool pauseStatus)
{
if (pauseStatus)
{
Debug.Log("OnApplicationPause: 遊戲暫停");
}
else
{
Debug.Log("OnApplicationPause: 遊戲恢復");
}
}
// OnDestroy 函數在物體被刪除時執行
void OnDestroy()
{
Debug.Log("OnDestroy: 物體被刪除");
}
// OnApplicationQuit 函數在玩家退出遊戲時執行
void OnApplicationQuit()
{
Debug.Log("OnApplicationQuit: 玩家退出遊戲");
}
// 自定義協程
IEnumerator MyCoroutine()
{
Debug.Log("Coroutine: 開始協程");
yield return new WaitForSeconds(2);
Debug.Log("Coroutine: 等待了 2 秒");
yield return new WaitForSeconds(2);
Debug.Log("Coroutine: 又等待了 2 秒");
}
}